/**
 * WebSocket manager for chat room page
 */
export class ChatWebSocketManager {
    constructor() {
        this.ws = null;
        this.url = null;
        this.isConnected = false;
        this.isConnecting = false;
        this.reconnectAttempts = 0;
        this.maxReconnectAttempts = 5;
        this.reconnectDelay = 1000;
        this.maxReconnectDelay = 30000;
        this.pingInterval = null;
        this.pingTimeout = null;
        this.pingIntervalDuration = 30000; // Default 30 seconds
        this.pingTimeoutDuration = 5000; // Default 5 seconds
        this.focusLossMode = false;
        
        // Event listeners
        this.listeners = new Map();
        
        // User and room state
        this.currentUser = null;
        this.currentRoom = null;
        
        // Message queue for offline messages
        this.messageQueue = [];
    }

    /**
     * Connect to WebSocket server
     */
    async connect(url) {
        if (this.isConnecting || this.isConnected) {
            return Promise.resolve();
        }

        this.url = url;
        this.isConnecting = true;

        return new Promise((resolve, reject) => {
            try {
                // Create WebSocket connection
                this.ws = new WebSocket(url);
                
                // Setup event handlers
                this.ws.onopen = (event) => {
                    this.handleOpen(event);
                    resolve();
                };
                
                this.ws.onmessage = (event) => {
                    this.handleMessage(event);
                };
                
                this.ws.onclose = (event) => {
                    this.handleClose(event);
                };
                
                this.ws.onerror = (event) => {
                    this.handleError(event);
                    if (this.isConnecting) {
                        reject(new Error('WebSocket connection failed'));
                    }
                };
                
                // Connection timeout
                setTimeout(() => {
                    if (this.isConnecting) {
                        this.ws.close();
                        reject(new Error('WebSocket connection timeout'));
                    }
                }, 10000);
                
            } catch (error) {
                this.isConnecting = false;
                reject(error);
            }
        });
    }

    /**
     * Disconnect from WebSocket server
     */
    disconnect() {
        this.stopPing();
        this.isConnected = false;
        this.isConnecting = false;
        
        if (this.ws) {
            this.ws.close();
            this.ws = null;
        }
        
        this.emit('disconnected');
    }

    /**
     * Handle WebSocket open event
     */
    handleOpen(event) {
        console.log('WebSocket connected');
        this.isConnected = true;
        this.isConnecting = false;
        this.reconnectAttempts = 0;
        
        this.startPing();
        this.processMessageQueue();
        this.emit('connected');
    }

    /**
     * Handle WebSocket message event
     */
    handleMessage(event) {
        try {
            const message = JSON.parse(event.data);
            this.processMessage(message);
        } catch (error) {
            console.error('Failed to parse WebSocket message:', error);
        }
    }

    /**
     * Handle WebSocket close event
     */
    handleClose(event) {
        console.log('WebSocket disconnected:', event.code, event.reason);
        
        this.isConnected = false;
        this.isConnecting = false;
        this.stopPing();
        
        this.emit('disconnected', { code: event.code, reason: event.reason });
        
        // Attempt to reconnect if not intentionally closed
        if (event.code !== 1000 && this.url) {
            this.scheduleReconnect();
        }
    }

    /**
     * Handle WebSocket error event
     */
    handleError(event) {
        console.error('WebSocket error:', event);
        this.emit('error', event);
    }

    /**
     * Process incoming message
     */
    processMessage(message) {
        const { type, data } = message;
        
        switch (type) {
            case 'connected':
                console.log('Server connection confirmed');
                break;
                
            case 'join_success':
                this.handleJoinSuccess(data);
                break;
                
            case 'user_joined':
                this.handleUserJoined(data);
                break;
                
            case 'user_left':
                this.handleUserLeft(data);
                break;
                
            case 'new_message':
                this.handleNewMessage(data);
                break;
                
            case 'room_update':
                this.handleRoomUpdate(data);
                break;
                
            case 'room_info':
                this.handleRoomInfo(data);
                break;
                
            case 'pong':
                this.handlePong(data);
                break;
                
            case 'error':
                this.handleServerError(data);
                break;
                
            case 'server_shutdown':
                this.handleServerShutdown(data);
                break;
                
            default:
                console.warn('Unknown message type:', type);
        }
        
        // Emit generic message event
        this.emit('message', message);
    }

    /**
     * Handle successful room join
     */
    handleJoinSuccess(data) {
        this.currentUser = data.user;
        this.currentRoom = data.room;
        
        console.log('Successfully joined room:', this.currentRoom.name);
        this.emit('joinSuccess', data);
    }

    /**
     * Handle user joined event
     */
    handleUserJoined(data) {
        console.log('User joined:', data.user.nickname);
        this.emit('userJoined', data);
    }

    /**
     * Handle user left event
     */
    handleUserLeft(data) {
        console.log('User left:', data.user.nickname);
        this.emit('userLeft', data);
    }

    /**
     * Handle new message
     */
    handleNewMessage(data) {
        this.emit('newMessage', data);
    }

    /**
     * Handle room update
     */
    handleRoomUpdate(data) {
        if (this.currentRoom) {
            this.currentRoom.members = data.users;
            this.currentRoom.memberCount = data.memberCount;
        }
        this.emit('roomUpdate', data);
    }

    /**
     * Handle room info
     */
    handleRoomInfo(data) {
        this.currentRoom = { ...this.currentRoom, ...data };
        this.emit('roomInfo', data);
    }

    /**
     * Handle pong response
     */
    handlePong(data) {
        if (this.pingTimeout) {
            clearTimeout(this.pingTimeout);
            this.pingTimeout = null;
        }
    }

    /**
     * Handle server error
     */
    handleServerError(data) {
        console.error('Server error:', data.message);
        this.emit('serverError', data);
    }

    /**
     * Handle server shutdown
     */
    handleServerShutdown(data) {
        console.log('Server is shutting down:', data.message);
        this.emit('serverShutdown', data);
        this.disconnect();
    }

    /**
     * Send message to server
     */
    send(type, data = {}) {
        const message = { type, data, timestamp: new Date().toISOString() };
        
        if (this.isConnected && this.ws.readyState === WebSocket.OPEN) {
            try {
                this.ws.send(JSON.stringify(message));
                return true;
            } catch (error) {
                console.error('Failed to send WebSocket message:', error);
                this.messageQueue.push(message);
                return false;
            }
        } else {
            // Queue message for later
            this.messageQueue.push(message);
            return false;
        }
    }

    /**
     * Join a room
     */
    joinRoom(roomId, nickname, password = null, userId = null) {
        return this.send('join_room', {
            roomId,
            nickname,
            password,
            userId // For reconnection
        });
    }

    /**
     * Leave current room
     */
    leaveRoom() {
        const success = this.send('leave_room');
        
        if (success) {
            this.currentUser = null;
            this.currentRoom = null;
        }
        
        return success;
    }

    /**
     * Send chat message
     */
    sendMessage(content) {
        if (!content || !content.trim()) {
            return false;
        }
        
        return this.send('chat_message', {
            content: content.trim()
        });
    }

    /**
     * Request room information
     */
    requestRoomInfo() {
        return this.send('get_room_info');
    }

    /**
     * Send ping to server
     */
    ping() {
        if (this.isConnected) {
            this.send('ping');
            
            // Set timeout for pong response with current settings
            this.pingTimeout = setTimeout(() => {
                console.warn('Ping timeout - connection may be lost');
                if (!this.focusLossMode) {
                    // Only disconnect immediately if not in focus loss mode
                    this.disconnect();
                    this.scheduleReconnect();
                }
            }, this.pingTimeoutDuration);
        }
    }

    /**
     * Adjust heartbeat intervals based on page state
     * @param {string} mode - 'normal', 'focus_loss', 'background', 'recovery'
     */
    adjustHeartbeat(mode) {
        this.focusLossMode = mode !== 'normal';
        
        switch (mode) {
            case 'normal':
                this.pingIntervalDuration = 30000; // 30 seconds
                this.pingTimeoutDuration = 5000; // 5 seconds
                break;
            case 'focus_loss':
                this.pingIntervalDuration = 60000; // 60 seconds
                this.pingTimeoutDuration = 15000; // 15 seconds
                break;
            case 'background':
                this.pingIntervalDuration = 120000; // 2 minutes
                this.pingTimeoutDuration = 30000; // 30 seconds
                break;
            case 'recovery':
                this.pingIntervalDuration = 10000; // 10 seconds
                this.pingTimeoutDuration = 3000; // 3 seconds
                break;
        }
        
        // Restart ping with new intervals if connected
        if (this.isConnected) {
            this.startPing();
        }
        
        console.log(`Heartbeat adjusted to ${mode} mode: ping=${this.pingIntervalDuration}ms, timeout=${this.pingTimeoutDuration}ms`);
    }

    /**
     * Validate connection integrity
     * @returns {Promise} Resolves when connection is validated
     */
    async validateConnection() {
        return new Promise((resolve, reject) => {
            if (!this.isConnected) {
                reject(new Error('Not connected'));
                return;
            }
            
            // Send validation ping with session context
            const validationId = 'validate_' + Date.now();
            
            this.send('validate_connection', {
                validationId,
                timestamp: Date.now(),
                reconnectAttempt: this.reconnectAttempts
            });
            
            // Listen for validation response
            const handleValidation = (message) => {
                if (message.type === 'connection_validated' && message.data.validationId === validationId) {
                    this.off('message', handleValidation);
                    resolve(message.data);
                }
            };
            
            const handleInvalid = (message) => {
                if (message.type === 'connection_invalid' && message.data.validationId === validationId) {
                    this.off('message', handleInvalid);
                    reject(new Error('Connection validation failed'));
                }
            };
            
            this.on('message', handleValidation);
            this.on('message', handleInvalid);
            
            // Timeout after 5 seconds
            setTimeout(() => {
                this.off('message', handleValidation);
                this.off('message', handleInvalid);
                reject(new Error('Validation timeout'));
            }, 5000);
        });
    }

    /**
     * Notify server about focus loss
     */
    notifyFocusLoss() {
        this.send('page_focus_lost', {
            timestamp: Date.now(),
            mode: 'hidden'
        });
    }

    /**
     * Notify server about focus regain
     */
    notifyFocusGain() {
        this.send('page_focus_gained', {
            timestamp: Date.now(),
            mode: 'visible'
        });
    }
    /**
     * Start ping interval with current settings
     */
    startPing() {
        this.stopPing();
        this.pingInterval = setInterval(() => {
            this.ping();
        }, this.pingIntervalDuration);
    }

    /**
     * Stop ping interval
     */
    stopPing() {
        if (this.pingInterval) {
            clearInterval(this.pingInterval);
            this.pingInterval = null;
        }
        
        if (this.pingTimeout) {
            clearTimeout(this.pingTimeout);
            this.pingTimeout = null;
        }
    }

    /**
     * Process queued messages
     */
    processMessageQueue() {
        while (this.messageQueue.length > 0 && this.isConnected) {
            const message = this.messageQueue.shift();
            try {
                this.ws.send(JSON.stringify(message));
            } catch (error) {
                console.error('Failed to send queued message:', error);
                // Put it back at the beginning of the queue
                this.messageQueue.unshift(message);
                break;
            }
        }
    }

    /**
     * Schedule reconnection attempt
     */
    scheduleReconnect() {
        if (this.reconnectAttempts >= this.maxReconnectAttempts) {
            console.error('Max reconnection attempts reached');
            this.emit('reconnectFailed');
            return;
        }
        
        this.reconnectAttempts++;
        const delay = Math.min(
            this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1),
            this.maxReconnectDelay
        );
        
        console.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
        this.emit('reconnecting', { attempt: this.reconnectAttempts, delay });
        
        setTimeout(() => {
            if (!this.isConnected && this.url) {
                this.connect(this.url).catch((error) => {
                    console.error('Reconnection failed:', error);
                    this.scheduleReconnect();
                });
            }
        }, delay);
    }

    /**
     * Add event listener
     */
    on(event, callback) {
        if (!this.listeners.has(event)) {
            this.listeners.set(event, []);
        }
        this.listeners.get(event).push(callback);
    }

    /**
     * Remove event listener
     */
    off(event, callback) {
        if (this.listeners.has(event)) {
            const callbacks = this.listeners.get(event);
            const index = callbacks.indexOf(callback);
            if (index !== -1) {
                callbacks.splice(index, 1);
            }
        }
    }

    /**
     * Emit event to listeners
     */
    emit(event, data = null) {
        if (this.listeners.has(event)) {
            this.listeners.get(event).forEach(callback => {
                try {
                    callback(data);
                } catch (error) {
                    console.error(`Error in ${event} listener:`, error);
                }
            });
        }
    }

    /**
     * Get connection status
     */
    getStatus() {
        return {
            isConnected: this.isConnected,
            isConnecting: this.isConnecting,
            reconnectAttempts: this.reconnectAttempts,
            currentUser: this.currentUser,
            currentRoom: this.currentRoom,
            queuedMessages: this.messageQueue.length
        };
    }

    /**
     * Clear all data
     */
    clear() {
        this.disconnect();
        this.currentUser = null;
        this.currentRoom = null;
        this.messageQueue = [];
        this.listeners.clear();
        this.url = null;
    }
}